﻿using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;

namespace RQX.Common.Web.Web.Container
{
    /// <summary>
    /// 自动注入，用于属性注入
    /// </summary>
    public class AutoInjectionService : IIoCManager
    {
        private readonly IServiceProvider _serviceProvider;
        Dictionary<Type, Action<object, IServiceProvider>> autoInjectionActions = new Dictionary<Type, Action<object, IServiceProvider>>();
        public AutoInjectionService(IServiceProvider serviceProvider)
        {
            _serviceProvider = serviceProvider;
        }

        public void AutoInjection(object service)
        {
            AutoInjection(service, _serviceProvider);
        }
        /// <summary>
        /// 装配属性和字段
        /// </summary>
        /// <param name="service"></param>
        /// <param name="serviceProvider"></param>
        public void AutoInjection(object service, IServiceProvider serviceProvider)
        {
            var serviceType = service.GetType();
            if (autoInjectionActions.TryGetValue(serviceType, out Action<object, IServiceProvider> act))
            {
                act(service, serviceProvider);
            }
            else
            {
                //参数
                var objParam = Expression.Parameter(typeof(object), "obj");
                var spParam = Expression.Parameter(typeof(IServiceProvider), "sp");

                var obj = Expression.Convert(objParam, serviceType);
                var GetService = typeof(IServiceProvider).GetMethod("GetService");
                List<Expression> setList = new List<Expression>();

                //字段赋值
                foreach (FieldInfo field in serviceType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
                {
                    var autowiredAttr = field.GetCustomAttribute<AutoInjectionAttribute>();
                    if (autowiredAttr != null)
                    {
                        var fieldExp = Expression.Field(obj, field);
                        var createService = Expression.Call(spParam, GetService, Expression.Constant(field.FieldType));
                        var setExp = Expression.Assign(fieldExp, Expression.Convert(createService, field.FieldType));
                        setList.Add(setExp);
                    }
                }
                //属性赋值
                foreach (PropertyInfo property in serviceType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
                {
                    var autowiredAttr = property.GetCustomAttribute<AutoInjectionAttribute>();
                    if (autowiredAttr != null)
                    {
                        var propExp = Expression.Property(obj, property);
                        var createService = Expression.Call(spParam, GetService, Expression.Constant(property.PropertyType));
                        var setExp = Expression.Assign(propExp, Expression.Convert(createService, property.PropertyType));
                        setList.Add(setExp);
                    }
                }
                var bodyExp = Expression.Block(setList);
                var setAction = Expression.Lambda<Action<object, IServiceProvider>>(bodyExp, objParam, spParam).Compile();
                autoInjectionActions[serviceType] = setAction;
                setAction(service, serviceProvider);
            }
        }
        //public void AutoInjection(object service)
        //{
        //    var serviceType = service.GetType();
        //    //字段赋值
        //    //foreach (FieldInfo field in serviceType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
        //    //{
        //    //    var autowiredAttr = field.GetCustomAttribute<AutowiredAttribute>();
        //    //    if (autowiredAttr != null)
        //    //    {
        //    //        field.SetValue(service, serviceProvider.GetService(field.FieldType));
        //    //    }
        //    //}
        //    //属性赋值
        //    foreach (PropertyInfo property in serviceType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
        //    {
        //        var autowiredAttr = property.GetCustomAttribute<AutoInjectionAttribute>();
        //        if (autowiredAttr != null)
        //        {
        //            property.SetValue(service, _serviceProvider.GetService(property.PropertyType));
        //        }
        //    }
        //}
    }
}
